/* Filename: susp.c
 * No.78.信号sigsuspend
 * Description:
 * 1.屏蔽信号
 * 2.打印5个*换行后
 * 3.取消信号屏蔽并且等信号
 * 4.收到信号后回到1
 * Last modified: humble 20200404 17:12
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#define _SAVESET_ (1)

#define demo (2) //0,1,2

static void usage(void);

void int_handler(int i)
{
    write(1, "!", 1);
}

int main(int argc, char **argv)
{
    int i;
    sigset_t set, oset, saveset;

    if(argc != 1){
        fprintf(stderr, "argc !=1");
        usage();
        exit(1);
    }

    signal(SIGINT, int_handler);
    sigemptyset(&set);
    sigaddset(&set, SIGINT);

#if _SAVESET_
    //此句目的在于保存当前mask到saveset，方便以后恢复，用SIG_UNBLOCK或SIG_BLOCK都无所谓
    sigprocmask(SIG_UNBLOCK, &set, &saveset);
#endif

#if (demo == 1) || (demo == 2)
    sigprocmask(SIG_BLOCK, &set, &oset); //block住
#endif

    while(1){
#if (demo == 0)
        sigprocmask(SIG_BLOCK, &set, &oset);
#endif

        for(i = 0; i < 5; i++)
        {
            write(1, "*", 1);
            sleep(1);
        }
        write(1, "\n", 1);

#if (demo == 0)
        sigprocmask(SIG_SETMASK, &oset, NULL);
        //因为sigprocmask和pause两个函数不原子
        //如果在sigprocmask和pause之间收到信号，那响应完信号之后进入了pause
        //可是我们期望它响应信号后能继续打印*
        pause();
#elif (demo == 1)
        sigset_t tmpset;
        sigprocmask(SIG_SETMASK, &oset, &tmpset); //恢复unblock，并用tmpset记录block集(set和tmpset相同)
        //sigprocmask和pause两个函数不原子，结果跟demo0一样
        pause();
        sigprocmask(SIG_SETMASK, &tmpset, NULL); //再次block
#elif (demo == 2)
        //sigsuspend 就相当于上面demo1的原子操作
        sigsuspend(&oset);//原子化 unblock后立刻进入pause(wait)
#endif
    }

#if _SAVESET_
    //把mask设置为saveset(相当于恢复了之前的状态)
    sigprocmask(SIG_SETMASK, &saveset, NULL);
#endif

    return 0;
}

static void usage(void)
{
#define USAGE "Usage:\n\
  Helloworld\n"
    printf(USAGE);
}
